use std::collections::{BTreeMap, HashMap};

use config::ValueKind;
use proptest::arbitrary::any;
use proptest::prelude::{prop, Strategy};
use proptest::{prop_assert, prop_compose, proptest};

use crate::tests::common::{
    arbitrary_btree_map_strategy,
    nil_string_value_kind_strategy,
    option_string_strategy,
    IDENTIFIER_STRING,
};
use crate::tiling::tiles::tile_list::process_connector;
use crate::Error;

fn arbitrary_connector_map_strategy() -> impl Strategy<Value = HashMap<Option<String>, usize>> {
    prop::collection::hash_map(option_string_strategy(), any::<usize>(), 0..30)
}

prop_compose! {
    fn valid_connectors_strategy()(
        north in nil_string_value_kind_strategy(),
        east in nil_string_value_kind_strategy(),
        south in nil_string_value_kind_strategy(),
        west in nil_string_value_kind_strategy()
    ) -> BTreeMap<String, ValueKind> {
        let mut map: BTreeMap<String, ValueKind> = BTreeMap::new();
        map.insert(String::from("north"), north);
        map.insert(String::from("east"), east);
        map.insert(String::from("south"), south);
        map.insert(String::from("west"), west);
        map
    }
}

proptest! {
    #[test]
    fn test_process_connector_does_not_panic(
        connectors in arbitrary_btree_map_strategy(),
        side in ".+",
        entry in arbitrary_btree_map_strategy(),
        name in ".+",
        mut connector_map in arbitrary_connector_map_strategy(),
        mut connector_names in proptest::collection::vec(IDENTIFIER_STRING, 0..10),
        mut current_connector_id in any::<usize>(),
    ) {
        let connectors: BTreeMap<String, &ValueKind> = connectors
            .iter()
            .map(|(side, value)| (side.clone(), value))
            .collect();
        let entry: BTreeMap<String, &ValueKind> = entry
            .iter()
            .map(|(side, value)| (side.clone(), value))
            .collect();

        _ = process_connector(
            &connectors,
            &side,
            &entry,
            &name,
            &mut connector_map,
            &mut connector_names,
            &mut current_connector_id,
        );
    }

    #[test]
    fn test_process_connector_with_valid_parameters(
        connectors in valid_connectors_strategy(),
        side in "north|east|south|west",
        entry in arbitrary_btree_map_strategy(),
        name in ".+",
        mut connector_map in arbitrary_connector_map_strategy(),
        mut connector_names in proptest::collection::vec(IDENTIFIER_STRING, 0..10),
        mut current_connector_id in any::<usize>(),
    ) {
        let connectors: BTreeMap<String, &ValueKind> = connectors
            .iter()
            .map(|(side, value)| (side.clone(), value))
            .collect();
        let entry: BTreeMap<String, &ValueKind> = entry
            .iter()
            .map(|(side, value)| (side.clone(), value))
            .collect();

        let result: Result<usize, Error> = process_connector(
            &connectors,
            &side,
            &entry,
            &name,
            &mut connector_map,
            &mut connector_names,
            &mut current_connector_id,
        );
        prop_assert!(result.is_ok());
    }
}
